package com.gz.common.mongodb.utils;

import com.gz.common.mongodb.base.MongoBaseModel;
import com.gz.common.mongodb.support.MongoField;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName MongoBaseRepository
 * @Description //
 * @Author WHX
 * @Date 2021/8/13 16:06
 **/
public class Mongo {
    private static MongoTemplate mongoTemplate = null;


    /**
     * 从spring容器中获取单例 mongoTemplate
     *
     * @return
     */
    public static MongoTemplate mongoTemplate() {
        if (mongoTemplate == null) {
            // 使用IOC容器中的单例
            mongoTemplate = SpringUtil.getBean(MongoTemplate.class);
        }
        return mongoTemplate;
    }


    /**
     * 构建条件 Query
     * 根据实体类中不为null的所有字段（字段名--字段值）作为查询条件
     * <p>
     * 注意：仅支持的字段类型为：基本类型，或基本类型的包装类，或字符串
     * 构建条件时，会忽略集合类型以及其他引用类型
     *
     * @param model
     * @return Criteria
     */
    public static Criteria getSelectiveCriteria(@Nullable MongoBaseModel model) {
        Criteria criteria = new Criteria();
        getSelectiveFieldsMap(model).forEach((fieldName, fieldValue) -> {
            criteria.and(fieldName).is(fieldValue);
        });
        return criteria;
    }


    /**
     * 构建条件 Update
     * 根据实体类中不为null的所有字段（字段名--字段值）作为修改字段
     * <p>
     * 注意：仅支持的字段类型为：基本类型，或基本类型的包装类，或字符串
     * 构建条件时，会忽略集合类型以及其他引用类型
     *
     * @param model
     * @return Query
     */
    public static Update getSelectiveUpdate(@Nullable MongoBaseModel model) {
        Update update = new Update();
        getSelectiveFieldsMap(model).forEach((fieldName, fieldValue) -> {
            update.set(fieldName, fieldValue);
        });
        return update;
    }

    /**
     * 获取不为null值的字段集合 （key=字段名, value=字段值）
     * <p>
     * 注意：仅支持的字段类型为：基本类型，或基本类型的包装类，或字符串
     * 构建条件时，会忽略集合类型以及其他引用类型
     * @param model
     * @return
     */
    private static Map<String, Object> getSelectiveFieldsMap(@Nullable MongoBaseModel model) {
        if (model == null) return new LinkedHashMap<>();

        List<Field> allFields = ClassUtil.getAllDeclaredFields(model.getClass());

        Map<String, Object> map = new LinkedHashMap<>();
        allFields.forEach(field -> {
            if (field.getName().equals("serialVersionUID")) return; // 忽略该字段
            if (!ClassUtil.isBasicClass(field.getType())) return; // 忽略其他类型 continue;
            Object fieldObj = null;
            try {
                ReflectionUtils.makeAccessible(field);
                fieldObj = field.get(model);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            if (fieldObj != null) map.put(field.getName(), fieldObj);
        });

        return map;
    }


    /**
     * 构建字段配置
     *
     * @param fields
     * @param mongoField
     */
    public static void buildFields(@NonNull org.springframework.data.mongodb.core.query.Field fields, @Nullable MongoField mongoField) {
        Map<String, Integer> criteria;
        if (mongoField == null || CollectionUtils.isEmpty(criteria = mongoField.getCriteria())) return;

        criteria.forEach((k, v) -> {
            if (v == 1) fields.include(k);
            if (v == 0) fields.exclude(k);
        });
    }
}
